import java.text.*;
import java.util.*;
import java.util.regex.*;

public class Validator {
	
	public static void main(String[] args) {
		//DEBUG
		System.out.println(isValidMessage("update<$dbversion=0,$peername=1333333333>"));
		System.out.println(isValidMessage("requestentry<$dbversion=0>"));
		System.out.println(isValidMessage("requestentry<$dbversion=04/03/09;13:02:33,$list(1222222222)>"));
		System.out.println(isValidMessage("requestentry<$dbversion=04/03/09;13:02:33,$list(1222222222,1333333333,1444444444)>"));
		System.out.println(isValidMessage("requestentry<$dbversion=04/03/09;13:02:33,$range(1222222222-1555555555)>"));
		System.out.println(isValidMessage("requestentry<$dbversion=04/03/09;13:02:33,$range(0,1333333333)>"));
		System.out.println(isValidMessage("requestentry<$dbversion=04/03/09;13:02:33,$range(1333333333,ALL)>"));

		System.out.println(isValidMessage("entry($sn=1,$n=17)<$dbversion=04/03/09;13:02:33,$peerversion=04/03/09;11:12:03,$peername=1222222222,$ip=123.123.123.123,$tcp=80,$udp=44,$phone=123456778,$email=ploni@biu.ac.il,$lastConnectionTime=04/03/09;13:02:33,$status=1,$picSize=10000,$picVersion=04/03/09;13:02:33>"));

		System.out.println(isValidMessage("requestpicture<$dbversion=0,$rcvWin=5000>"));
		System.out.println(isValidMessage("requestpicture<$dbversion=04/03/09;13:02:33,$rcvWin=5000,$peerName=1222222222>"));
		System.out.println(isValidMessage("requestpicture<$dbversion=04/03/09;13:02:33,$rcvWin=5000,$peerName=1222222222,$list(i)>"));
		System.out.println(isValidMessage(	"requestpicture<$dbversion=04/03/09;13:02:33,$rcvWin=5000,$peerName=1222222222,$list(i,j,k)>"));
		System.out.println(isValidMessage(	"requestpicture<$dbversion=04/03/09;13:02:33,$rcvWin=5000,$peerName=1222222222,$range(i-j)>"));

		System.out.println(isValidMessage("picture($sn=1,$n=17)<$picversion=04/03/09;13:02:33,$peername=1222222222,$data=encoded picture>"));
	}
	
	private static boolean isDate(String str) {
		if (str.equals("0")) //Support 0 date
			return true;
		
		String[] parsedDate = str.split(":|/|;");
		if (parsedDate.length != 6)
			return false;
		
		for (int i = 0; i < 6; ++i)
			if (parsedDate[i].length() != 2)
				return false;
		
		return true;
	}
	
	/**
	 * Validates a message 
	 * @param message
	 * @return
	 */
	public static boolean isValidMessage(String message) {
		DateFormat _dateFormatter = new SimpleDateFormat("dd/MM/yyyy;HH:mm:ss");
		
		if (!message.endsWith(">"))
			return false;
		
		String[] parsed = Utils.parseMessage(message);
		
		if (message.startsWith("update<")) {
			
			if (parsed.length != 2)
				return false;
			
			if (!parsed[0].equals("0"))
				try {_dateFormatter.parse(parsed[0]);}
				catch(ParseException ex) {
					return false;
				}
			
			return isDate(parsed[0]);
			
		} else if (message.startsWith("requestentry<")) {
			
			if (Utils.StringToDate(parsed[0]) == null || !isDate(parsed[0]))
				return false;
			
			if (parsed.length == 1) 
				return isDate(parsed[0]);
			
			if (!parsed[1].equals("list") && !parsed[1].equals("range"))
				return false;
			
			return true;
		} else if (message.startsWith("requestpicture<")) {
			
			if (Utils.StringToDate(parsed[0]) == null || !isDate(parsed[0]))
				return false;
			
			if (parsed.length < 2) //3
				return false;
			
			try{
				if (Integer.parseInt(parsed[1]) < 0)
					return false;
			} catch (NumberFormatException ex) {
				return false;
			}
			
			if (parsed.length <= 3)
				return true;
			
			if (!parsed[3].equals("list") && !parsed[3].equals("range"))
				return false;
			
			return true;
			
		} else if (message.startsWith("entry(")) {
			if (parsed.length != 14)
				return false;
			
			try {
				if (Integer.parseInt(parsed[0]) <= 0) //sn
					return false;
				
				if (Integer.parseInt(parsed[1])<=0) //n
					return false;
				
				if (Utils.StringToDate(parsed[2]) == null || !isDate(parsed[2]) || parsed[2].equals("0")) //dbVersion
					return false;
				
				if (Utils.StringToDate(parsed[3]) == null || !isDate(parsed[3]) || parsed[3].equals("0")) //peerVersion
					return false;
				 
				if (!isIP(parsed[5])) //IP
					return false;
				
				int num = Integer.parseInt(parsed[6]); //TCP
				if (num <=0 || num >= 65536)
					return false;
				
				num = Integer.parseInt(parsed[7]); //UDP
				if (num <=0 || num >= 65536)
					return false;
				
				if (!isEmail(parsed[9])) //Email
					return false;
				 
				
				if (Utils.StringToDate(parsed[10]) == null || !isDate(parsed[10])) //Last connection time
					return false;
				
				if (Integer.parseInt(parsed[11]) != 0 && Integer.parseInt(parsed[11]) != 1) //status
					return false;
				
				if (Integer.parseInt(parsed[12]) <= 0) //Pic size
						return false;
				
				if (Utils.StringToDate(parsed[13]) == null || !isDate(parsed[13]) || parsed[13].equals("0")) //Pic version
					return false;
				
			} catch (NumberFormatException ex) {
				return false;
			} 
			
			return true;
		} else if (message.startsWith("picture(")) {
			if (parsed.length != 5)
				return false;
			
			try {
				if (Integer.parseInt(parsed[0]) <= 0) //sn
					return false;
				
				if (Integer.parseInt(parsed[1])<=0) //n
					return false;
				
				if (!isDate(parsed[2]) || parsed[2].equals("0")) //Pic version
					return false;
				
			} catch (NumberFormatException ex) {
				return false;
			}
			
			return true;
		}
		
		return false;
	}

	/**
	 * Decides if date1 is older than date 2
	 * @param date1
	 * @param date2
	 * @return
	 */
	public static boolean isOlderDate(String date1, String date2) {
		Date d1, d2;
		DateFormat _dateFormatter = new SimpleDateFormat("dd/MM/yyyy;HH:mm:ss");
		
		try {
			d1 = _dateFormatter.parse(date1);
			d2 = _dateFormatter.parse(date2);
		} catch (ParseException ex) {
			return false;
		}
		
		if (d1.compareTo(d2) != 1)
			return true;
		return false;
	}

	public static boolean isEmail(String str) {
		String regex = "([a-zA-Z0-9]+)@(([a-zA-Z0-9]+).)+[a-zA-Z0-9]+";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		
		return matcher.find();
	}
	
	
	public static boolean isIP(String str) {
		
		String[] parsed = str.split("\\.");
		
		if (parsed.length != 4)
			return false;
		
		int num1 = Integer.parseInt(parsed[0]);
		if (num1 < 1 || num1 > 255)
			return false;
		
		int num2 = Integer.parseInt(parsed[1]);
		if (num2 < 0 || num2 > 255)
			return false;
		
		int num3 = Integer.parseInt(parsed[2]);
		if (num3 < 0 || num3 > 255)
			return false;
		
		int num4 = Integer.parseInt(parsed[3]);
		if (num4 < 0 || num4 > 255)
				return false;
			
		return true;
	}
}
